#include <iostream>
#include <vector>
#include <string>
#include <unistd.h>
#include <cstdlib>
#include <cassert>
#include <ctime>
#include <sys/wait.h>
#include <sys/types.h>
using namespace std;
#define PROCESS_NUM 5
#define Make_Seed() srand((unsigned long)time(nullptr) ^ getpid() ^ 0X3f ^ rand() % 1234)
typedef void (*func_t)(); // 函数指针类型
// 子进程需要完成的任务
void downLodeTask()
{
    cout << getpid() << " 下载任务" << endl;
    sleep(1);
}
void ioTask()
{
    cout << getpid() << " IO任务" << endl;
    sleep(1);
}
void flushTask()
{
    cout << getpid() << " 刷新任务" << endl;
    sleep(1);
}
// 多进程代码
class sunEndPoint
{
public:
    sunEndPoint(pid_t subId, int writeFd)
        : _subId(subId), _writeFd(writeFd)
    {
        char namebuffer[1024];
        snprintf(namebuffer, sizeof(namebuffer), "process-%d[pid(%d)-fd(%d)]", num++, _subId, _writeFd);
        _name = namebuffer;
    }

public:
    string _name;
    pid_t _subId; // pid
    int _writeFd; // 写fd
    static int num;
};
int sunEndPoint::num = 0;
void loadTaskFunc(vector<func_t> *out)
{
    assert(out);
    out->push_back(downLodeTask);
    out->push_back(ioTask);
    out->push_back(flushTask);
}
int recvTask(int readFd)
{
    int code = 0;
    ssize_t s = read(readFd, &code, sizeof(code));
    if (s == sizeof(code)) // 合法信息
    {
        return code;
    }
    else if (s <= 0)
    {
        return -1;
    }
    else
        return 0;
}
void createSubProcess(vector<sunEndPoint> *subs, vector<func_t> &funcMap)
{
    vector<int> deleteFd; // 解决下一个子进程拷贝父进程读端的问题
    for (int i = 0; i < PROCESS_NUM; ++i)
    {
        int fds[2];
        int n = pipe(fds);
        assert(n == 0);
        (void)n;
        pid_t id = fork();
        if (id == 0) // 子进程
        {
            // 关闭上一个文件的写端文件描述符
            for (int i = 0; i < deleteFd.size(); ++i)
            {
                close(deleteFd[i]);
            }
            // 子进程，处理任务
            close(fds[1]);
            while (true)
            {
                // 1、获取命令码，如果父进程没有发送，子进程被阻塞
                int commandCode = recvTask(fds[0]);
                // 2、完成任务
                if (commandCode >= 0 && commandCode < funcMap.size())
                {
                    funcMap[commandCode]();
                }
                else if (commandCode == -1)
                {
                    break;
                }
            }
            exit(0);
        }
        // 父进程
        close(fds[0]);
        sunEndPoint sub(id, fds[1]);
        subs->push_back(sub);

        deleteFd.push_back(fds[1]);
    }
}
void sendTask(const sunEndPoint &process, int taskNum)
{
    cout << "send task num" << taskNum << " send to->" << process._name << endl;
    int n = write(process._writeFd, &taskNum, sizeof(taskNum));
    assert(n == sizeof(int)); // 判断是否成功写入4个字节
    (void)n;
}
void loadBlanceContrl(const vector<sunEndPoint> &subs, const vector<func_t> &funcMap, int count)
{
    int processnum = subs.size(); // 子进程的个数
    int tasknum = funcMap.size();
    bool forever = (count == 0 ? true : false);
    while (true)
    {
        // 选择一个子进程,从vector<sunEndPoint>选择一个index
        int subIdx = rand() % processnum;
        // 选择一个任务,从vector<func_t>选择一个index
        int taskIdx = rand() % tasknum;
        // 将任务发送给指定的子进程,将一个任务的下标发送给子进程
        sendTask(subs[subIdx], taskIdx); // taskIdx作为管道的大小4个字节
        sleep(1);
        if (!forever) // forever不为0
        {
            --count;
            if (count == 0)
                break;
        }
    }
    // 写端退出，读端将管道内数据读完后read返回0
    for (int i = 0; i < processnum; ++i)
    {
        close(subs[i]._writeFd); // 最晚被创建的子进程拥有早期创建的子进程的父进程的读端，所以这里其实是后创建的进程先关闭
    }
}
// 回收子进程
void waitProcess(vector<sunEndPoint> processes)
{
    int processnum = processes.size();
    for (int i = 0; i < processnum; ++i)
    {
        waitpid(processes[i]._subId, nullptr, 0);
        cout << "wait sub process success " << processes[i]._subId << endl;
    }
}
// 父进程给子进程发布命令，父进程写，子进程读
int main()
{
    Make_Seed(); // 创建随机数
    // 父进程创建子进程及和子进程通信的管道
    vector<func_t> funcMap;           // vector<函数指针> funcMap
    loadTaskFunc(&funcMap);           // 加载任务
    vector<sunEndPoint> subs;         // 子进程集合
    createSubProcess(&subs, funcMap); // 维护父子通信信道
    // 这里的程序是父进程，用于控制子进程
    int taskCnt = 9; // 让子进程做9个任务
    loadBlanceContrl(subs, funcMap, taskCnt);
    // 回收子进程信息
    waitProcess(subs);
    return 0;
}